
package cn.iocoder.yudao.framework.mybatis.core.mapper;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.pojo.SortingField;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.interfaces.MPJBaseJoin;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Param;

import java.util.Collection;
import java.util.List;

/**
 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展，提供更多的能力
 *
 * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口，提供基础的 CRUD 能力
 * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口，提供连表 Join 能力
 */
public interface BaseMapperX<T> extends MPJBaseMapper<T> {

    /**
     * 分页查询（支持排序）
     *
     * @param pageParam   分页参数对象，包含当前页码、每页大小等信息
     * @param queryWrapper 查询条件构造器
     * @return 分页结果集，包括总记录数和当前页的数据列表
     */
    default PageResult<T> selectPage(SortablePageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
        return selectPage(pageParam, pageParam.getSortingFields(), queryWrapper);
    }

    /**
     * 分页查询（不带排序字段）
     *
     * @param pageParam   分页参数对象，包含当前页码、每页大小等信息
     * @param queryWrapper 查询条件构造器
     * @return 分页结果集，包括总记录数和当前页的数据列表
     */
    default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
        return selectPage(pageParam, null, queryWrapper);
    }

    /**
     * 根据分页参数与排序字段进行分页查询
     *
     * @param pageParam       分页参数对象
     * @param sortingFields   排序字段集合
     * @param queryWrapper    查询条件构造器
     * @return 分页结果集，包括总记录数和当前页的数据列表
     */
    default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) {
        // 特殊：不分页，直接查询全部
        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
            MyBatisUtils.addOrder(queryWrapper, sortingFields);
            List<T> list = selectList(queryWrapper);
            return new PageResult<>(list, (long) list.size());
        }

        // MyBatis Plus 查询
        IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields);
        selectPage(mpPage, queryWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    /**
     * 连表分页查询并转换为目标类型的结果
     *
     * @param pageParam      分页参数对象
     * @param clazz          返回结果的目标实体类
     * @param lambdaWrapper  连表查询条件包装器
     * @param <D>            结果泛型类型
     * @return 分页结果集，包含目标类型的记录及总数
     */
    default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
        // 特殊：不分页，直接查询全部
        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
            List<D> list = selectJoinList(clazz, lambdaWrapper);
            return new PageResult<>(list, (long) list.size());
        }

        // MyBatis Plus Join 查询
        IPage<D> mpPage = MyBatisUtils.buildPage(pageParam);
        mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    /**
     * 使用自定义连接查询包装器执行分页查询，并将结果映射到指定类型
     *
     * @param pageParam         分页参数对象
     * @param resultTypeClass   映射后的结果类型
     * @param joinQueryWrapper  自定义连接查询包装器
     * @param <DTO>             DTO 泛型类型
     * @return 分页结果集，包含目标类型的记录及总数
     */
    default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {
        IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam);
        selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);
        // 转换返回
        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    /**
     * 根据单个字段值精确匹配查询一条记录
     *
     * @param field 字段名称
     * @param value 字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectOne(String field, Object value) {
        return selectOne(new QueryWrapper<T>().eq(field, value));
    }

    /**
     * 使用 Lambda 表达式方式根据单个字段值精确匹配查询一条记录
     *
     * @param field 字段对应的 Lambda 函数引用
     * @param value 字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectOne(SFunction<T, ?> field, Object value) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field, value));
    }

    /**
     * 根据两个字段值同时精确匹配查询一条记录
     *
     * @param field1 第一个字段名
     * @param value1 第一个字段值
     * @param field2 第二个字段名
     * @param value2 第二个字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectOne(String field1, Object value1, String field2, Object value2) {
        return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    /**
     * 使用 Lambda 表达式方式根据两个字段值同时精确匹配查询一条记录
     *
     * @param field1 第一个字段对应的 Lambda 函数引用
     * @param value1 第一个字段值
     * @param field2 第二个字段对应的 Lambda 函数引用
     * @param value2 第二个字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    /**
     * 使用 Lambda 表达式方式根据三个字段值同时精确匹配查询一条记录
     *
     * @param field1 第一个字段对应的 Lambda 函数引用
     * @param value1 第一个字段值
     * @param field2 第二个字段对应的 Lambda 函数引用
     * @param value2 第二个字段值
     * @param field3 第三个字段对应的 Lambda 函数引用
     * @param value3 第三个字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
                        SFunction<T, ?> field3, Object value3) {
        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
    }

    /**
     * 获取满足条件的第 1 条记录
     *
     * 目的：解决并发场景下，插入多条记录后，使用 selectOne 会报错的问题
     *
     * @param field 字段名
     * @param value 字段值
     * @return 实体
     */
    default T selectFirstOne(SFunction<T, ?> field, Object value) {
        // 如果明确使用 MySQL 等场景，可以考虑使用 LIMIT 1 进行优化
        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field, value));
        return CollUtil.getFirst(list);
    }

    /**
     * 根据两个字段值同时精确匹配查询第一条记录
     *
     * @param field1 第一个字段对应的 Lambda 函数引用
     * @param value1 第一个字段值
     * @param field2 第二个字段对应的 Lambda 函数引用
     * @param value2 第二个字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectFirstOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
        return CollUtil.getFirst(list);
    }

    /**
     * 根据三个字段值同时精确匹配查询第一条记录
     *
     * @param field1 第一个字段对应的 Lambda 函数引用
     * @param value1 第一个字段值
     * @param field2 第二个字段对应的 Lambda 函数引用
     * @param value2 第二个字段值
     * @param field3 第三个字段对应的 Lambda 函数引用
     * @param value3 第三个字段值
     * @return 匹配的第一条记录或 null
     */
    default T selectFirstOne(SFunction<T,?> field1, Object value1, SFunction<T,?> field2, Object value2,
                             SFunction<T,?> field3, Object value3) {
        List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
        return CollUtil.getFirst(list);
    }

    /**
     * 统计所有记录的数量
     *
     * @return 总记录数
     */
    default Long selectCount() {
        return selectCount(new QueryWrapper<>());
    }

    /**
     * 根据单个字段值统计符合条件的记录数量
     *
     * @param field 字段名称
     * @param value 字段值
     * @return 符合条件的记录数量
     */
    default Long selectCount(String field, Object value) {
        return selectCount(new QueryWrapper<T>().eq(field, value));
    }

    /**
     * 使用 Lambda 表达式方式根据单个字段值统计符合条件的记录数量
     *
     * @param field 字段对应的 Lambda 函数引用
     * @param value 字段值
     * @return 符合条件的记录数量
     */
    default Long selectCount(SFunction<T, ?> field, Object value) {
        return selectCount(new LambdaQueryWrapper<T>().eq(field, value));
    }

    /**
     * 查询所有记录
     *
     * @return 所有记录组成的列表
     */
    default List<T> selectList() {
        return selectList(new QueryWrapper<>());
    }

    /**
     * 根据单个字段值精确匹配查询记录列表
     *
     * @param field 字段名称
     * @param value 字段值
     * @return 符合条件的记录列表
     */
    default List<T> selectList(String field, Object value) {
        return selectList(new QueryWrapper<T>().eq(field, value));
    }

    /**
     * 使用 Lambda 表达式方式根据单个字段值精确匹配查询记录列表
     *
     * @param field 字段对应的 Lambda 函数引用
     * @param value 字段值
     * @return 符合条件的记录列表
     */
    default List<T> selectList(SFunction<T, ?> field, Object value) {
        return selectList(new LambdaQueryWrapper<T>().eq(field, value));
    }

    /**
     * 根据某个字段在给定集合中的值查询记录列表
     *
     * @param field  字段名称
     * @param values 值集合
     * @return 符合条件的记录列表
     */
    default List<T> selectList(String field, Collection<?> values) {
        if (CollUtil.isEmpty(values)) {
            return CollUtil.newArrayList();
        }
        return selectList(new QueryWrapper<T>().in(field, values));
    }

    /**
     * 使用 Lambda 表达式方式根据某个字段在给定集合中的值查询记录列表
     *
     * @param field  字段对应的 Lambda 函数引用
     * @param values 值集合
     * @return 符合条件的记录列表
     */
    default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {
        if (CollUtil.isEmpty(values)) {
            return CollUtil.newArrayList();
        }
        return selectList(new LambdaQueryWrapper<T>().in(field, values));
    }

    /**
     * 根据两个字段值同时精确匹配查询记录列表
     *
     * @param field1 第一个字段对应的 Lambda 函数引用
     * @param value1 第一个字段值
     * @param field2 第二个字段对应的 Lambda 函数引用
     * @param value2 第二个字段值
     * @return 符合条件的记录列表
     */
    default List<T> selectList(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
        return selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
    }

    /**
     * 批量插入，适合大量数据插入
     *
     * @param entities 实体们
     */
    default Boolean insertBatch(Collection<T> entities) {
        // 特殊：SQL Server 批量插入后，获取 id 会报错，因此通过循环处理
        DbType dbType = JdbcUtils.getDbType();
        if (JdbcUtils.isSQLServer(dbType)) {
            entities.forEach(this::insert);
            return CollUtil.isNotEmpty(entities);
        }
        return Db.saveBatch(entities);
    }

    /**
     * 批量插入，适合大量数据插入
     *
     * @param entities 实体们
     * @param size     插入数量 Db.saveBatch 默认为 1000
     */
    default Boolean insertBatch(Collection<T> entities, int size) {
        // 特殊：SQL Server 批量插入后，获取 id 会报错，因此通过循环处理
        DbType dbType = JdbcUtils.getDbType();
        if (JdbcUtils.isSQLServer(dbType)) {
            entities.forEach(this::insert);
            return CollUtil.isNotEmpty(entities);
        }
        return Db.saveBatch(entities, size);
    }

    /**
     * 更新所有记录（无过滤条件）
     *
     * @param update 待更新的实体对象
     * @return 受影响的行数
     */
    default int updateBatch(T update) {
        return update(update, new QueryWrapper<>());
    }

    /**
     * 根据 ID 批量更新实体对象
     *
     * @param entities 待更新的实体集合
     * @return 是否成功更新
     */
    default Boolean updateBatch(Collection<T> entities) {
        return Db.updateBatchById(entities);
    }

    /**
     * 根据 ID 批量更新实体对象（可设置批次大小）
     *
     * @param entities 待更新的实体集合
     * @param size     每次批量操作的数量，默认为 1000
     * @return 是否成功更新
     */
    default Boolean updateBatch(Collection<T> entities, int size) {
        return Db.updateBatchById(entities, size);
    }

    /**
     * 删除指定字段等于特定值的所有记录
     *
     * @param field 字段名称
     * @param value 字段值
     * @return 删除的记录数
     */
    default int delete(String field, String value) {
        return delete(new QueryWrapper<T>().eq(field, value));
    }

    /**
     * 使用 Lambda 表达式删除指定字段等于特定值的所有记录
     *
     * @param field 字段对应的 Lambda 函数引用
     * @param value 字段值
     * @return 删除的记录数
     */
    default int delete(SFunction<T, ?> field, Object value) {
        return delete(new LambdaQueryWrapper<T>().eq(field, value));
    }

    /**
     * 批量删除指定字段值在给定集合内的记录
     *
     * @param field  字段对应的 Lambda 函数引用
     * @param values 字段值集合
     * @return 删除的记录数
     */
    default int deleteBatch(SFunction<T, ?> field, Collection<?> values) {
        if (CollUtil.isEmpty(values)) {
            return 0;
        }
        return delete(new LambdaQueryWrapper<T>().in(field, values));
    }

}